#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xutil.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "libsendemail.h"

// Constants
#define WIDTH 800
#define HEIGHT 600
#define FONT_SIZE 30
#define INPUT_HEIGHT 35
#define BUTTON_HEIGHT 40
#define MARGIN 20
#define TEXT_PADDING 5

// Input field types
typedef enum
{
    INPUT_RECIPIENT,
    INPUT_SUBJECT,
    INPUT_MESSAGE,
    INPUT_ATTACHMENT,
    INPUT_NONE
} InputField;

// UI Element: Rectangle
typedef struct
{
    int x, y, w, h;
} Rect;

// UI State
typedef struct
{
    // Input fields
    char recipient[256];
    char subject[256];
    char message[2048];
    char attachment[256];
    char status[512];

    // Input state
    InputField activeField;
    int caretPos[4];
    bool showCaret;
    time_t caretTimer;

    // Buttons
    Rect btnAddRecipient;
    Rect btnAddAttachment;
    Rect btnSend;
    Rect btnClear;

    // Input field positions
    Rect rectRecipient;
    Rect rectSubject;
    Rect rectMessage;
    Rect rectAttachment;
    Rect rectStatus;

    // Font
    FT_Library ftLib;
    FT_Face face;
    unsigned char *fontBuffer;

    // X11
    Display *disp;
    Window win;
    GC gc;
    Colormap cmap;
    XColor colorBlack, colorWhite, colorGray, colorLightGray;
    XColor colorBlue, colorGreen, colorRed, colorDarkBlue;

    // Email state (includes SMTP config)
    EmailState *emailState;
    bool ignoreInterrupts;
} AppState;

// Helper: Safe string duplication
static char *safe_strdup(const char *src)
{
    if (!src)
        return NULL;
    size_t len = strlen(src) + 1;
    char *dest = (char *)malloc(len);
    if (dest)
        strcpy(dest, src);
    return dest;
}

// Initialize X11 colors
void initColors(AppState *app)
{
    app->cmap = DefaultColormap(app->disp, 0);

    XParseColor(app->disp, app->cmap, "black", &app->colorBlack);
    XParseColor(app->disp, app->cmap, "white", &app->colorWhite);
    XParseColor(app->disp, app->cmap, "#aaaaaa", &app->colorGray);
    XParseColor(app->disp, app->cmap, "#f0f0f0", &app->colorLightGray);
    XParseColor(app->disp, app->cmap, "#0066cc", &app->colorBlue);
    XParseColor(app->disp, app->cmap, "#009933", &app->colorGreen);
    XParseColor(app->disp, app->cmap, "#cc0000", &app->colorRed);
    XParseColor(app->disp, app->cmap, "#003366", &app->colorDarkBlue);

    XAllocColor(app->disp, app->cmap, &app->colorBlack);
    XAllocColor(app->disp, app->cmap, &app->colorWhite);
    XAllocColor(app->disp, app->cmap, &app->colorGray);
    XAllocColor(app->disp, app->cmap, &app->colorLightGray);
    XAllocColor(app->disp, app->cmap, &app->colorBlue);
    XAllocColor(app->disp, app->cmap, &app->colorGreen);
    XAllocColor(app->disp, app->cmap, &app->colorRed);
    XAllocColor(app->disp, app->cmap, &app->colorDarkBlue);
}

// Load system font with FreeType
bool loadFont(AppState *app)
{
    if (FT_Init_FreeType(&app->ftLib) != 0)
    {
        strcpy(app->status, "FreeType init failed");
        return false;
    }

    const char *fontPaths[] = {
        "/usr/share/fonts/truetype/msttcorefonts/Arial.ttf",
        "/Library/Fonts/Arial.ttf", "C:/Windows/Fonts/arial.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", NULL};

    for (int i = 0; fontPaths[i]; i++)
    {
        if (FT_New_Face(app->ftLib, fontPaths[i], 0, &app->face) == 0)
        {
            FT_Set_Pixel_Sizes(app->face, 0, FONT_SIZE);
            sprintf(app->status, "Loaded font: %s", fontPaths[i]);
            return true;
        }
    }

    strcpy(app->status, "No font found");
    return false;
}

// Initialize UI layout
void initLayout(AppState *app)
{
    // Input fields
    app->rectRecipient = (Rect){MARGIN, 100, WIDTH - 2 * MARGIN, INPUT_HEIGHT};
    app->rectSubject = (Rect){MARGIN, 150, WIDTH - 2 * MARGIN, INPUT_HEIGHT};
    app->rectMessage = (Rect){MARGIN, 200, WIDTH - 2 * MARGIN, INPUT_HEIGHT};
    app->rectAttachment = (Rect){MARGIN, 250, WIDTH - 2 * MARGIN, INPUT_HEIGHT};
    app->rectStatus = (Rect){MARGIN, 400, WIDTH - 2 * MARGIN, 120};

    // Buttons
    app->btnAddRecipient = (Rect){MARGIN, 320, 150, BUTTON_HEIGHT};
    app->btnAddAttachment = (Rect){200, 320, 170, BUTTON_HEIGHT};
    app->btnSend = (Rect){420, 320, 100, BUTTON_HEIGHT};
    app->btnClear = (Rect){570, 320, 100, BUTTON_HEIGHT};
}

// Initialize application with hardcoded SMTP config
bool initApp(AppState *app)
{
    // X11 setup
    app->disp = XOpenDisplay(NULL);
    if (!app->disp)
    {
        fprintf(stderr, "Cannot open display\n");
        return false;
    }

    int screen = DefaultScreen(app->disp);
    app->win = XCreateSimpleWindow(
        app->disp, DefaultRootWindow(app->disp), 100, 100, WIDTH, HEIGHT, 1,
        BlackPixel(app->disp, screen), WhitePixel(app->disp, screen));

    XSelectInput(app->disp, app->win,
                 ExposureMask | KeyPressMask | ButtonPressMask |
                     StructureNotifyMask);
    app->gc = XCreateGC(app->disp, app->win, 0, NULL);
    XMapWindow(app->disp, app->win);
    XStoreName(app->disp, app->win, "Email Sender");

    // Initialize colors and layout
    initColors(app);
    initLayout(app);

    // Font setup
    if (!loadFont(app))
        return false;

    // Input state initialization
    memset(app->recipient, 0, sizeof(app->recipient));
    memset(app->subject, 0, sizeof(app->subject));
    memset(app->message, 0, sizeof(app->message));
    memset(app->attachment, 0, sizeof(app->attachment));
    app->activeField = INPUT_NONE;
    app->caretPos[INPUT_RECIPIENT] = 0;
    app->caretPos[INPUT_SUBJECT] = 0;
    app->caretPos[INPUT_MESSAGE] = 0;
    app->caretPos[INPUT_ATTACHMENT] = 0;
    app->showCaret = true;
    app->caretTimer = time(NULL);

    // --------------------------
    // Hardcoded SMTP configuration (your values)
    // --------------------------
    app->emailState = SendEmailCreateState();
    if (!app->emailState)
    {
        strcpy(app->status, "Failed to create EmailState");
        return false;
    }

    // Set SMTP fields exactly as specified
    app->emailState->sender_email = safe_strdup("1690544550@qq.com");
    app->emailState->sender_key = safe_strdup("znxogkkkexpbeeaj");
    app->emailState->smtp_server = safe_strdup("smtp.qq.com");
    app->emailState->smtp_port = 465;

    // Validate config allocation
    if (!app->emailState->sender_email || !app->emailState->sender_key ||
        !app->emailState->smtp_server)
    {
        strcpy(app->status, "Failed to allocate SMTP config");
        return false;
    }

    app->ignoreInterrupts = false;
    sprintf(app->status, "SMTP config loaded: %s@%s:%d",
            app->emailState->sender_email, app->emailState->smtp_server,
            app->emailState->smtp_port);

    return true;
}

// Draw rectangle
void drawRect(AppState *app, Rect r, XColor color)
{
    XSetForeground(app->disp, app->gc, color.pixel);
    XFillRectangle(app->disp, app->win, app->gc, r.x, r.y, r.w, r.h);
}

// Draw rectangle border
void drawRectBorder(AppState *app, Rect r, XColor color)
{
    XSetForeground(app->disp, app->gc, color.pixel);
    XDrawRectangle(app->disp, app->win, app->gc, r.x, r.y, r.w, r.h);
}

// Render single glyph
void drawGlyph(AppState *app, FT_GlyphSlot slot, int x, int y)
{
    FT_Bitmap *bmp = &slot->bitmap;
    int x0 = x + slot->bitmap_left;
    int y0 = y - slot->bitmap_top;

    for (int row = 0; row < bmp->rows; row++)
    {
        for (int col = 0; col < bmp->width; col++)
        {
            int pixelX = x0 + col;
            int pixelY = y0 + row;
            if (pixelX >= 0 && pixelX < WIDTH && pixelY >= 0 && pixelY < HEIGHT)
            {
                unsigned char val = bmp->buffer[row * bmp->width + col];
                if (val > 0)
                {
                    XSetForeground(app->disp, app->gc, app->colorBlack.pixel);
                    XDrawPoint(app->disp, app->win, app->gc, pixelX, pixelY);
                }
            }
        }
    }
}

// Draw text using FreeType
int drawText(AppState *app, const char *text, int x, int y)
{
    int penX = x;
    int penY = y + FONT_SIZE;

    for (int i = 0; text[i]; i++)
    {
        if (FT_Load_Char(app->face, text[i], FT_LOAD_RENDER) != 0)
            continue;

        drawGlyph(app, app->face->glyph, penX, penY);
        penX += app->face->glyph->advance.x >> 6;
    }

    return penX;
}

// Draw input field
void drawInputField(AppState *app, const char *label, const char *text, Rect r,
                    InputField field)
{
    drawRect(app, r, app->colorLightGray);

    if (app->activeField == field)
    {
        drawRectBorder(app, r, app->colorBlue);
    }
    else
    {
        drawRectBorder(app, r, app->colorGray);
    }

    drawText(app, label, r.x, r.y - 20);
    drawText(app, text, r.x + TEXT_PADDING, r.y + (r.h - FONT_SIZE) / 2);

    if (app->activeField == field && app->showCaret)
    {
        char *caretText = malloc(app->caretPos[field] + 1);
        if (caretText)
        {
            strncpy(caretText, text, app->caretPos[field]);
            caretText[app->caretPos[field]] = '\0';

            int caretX = r.x + TEXT_PADDING + drawText(app, caretText, 0, 0);
            int caretY1 = r.y + 5;
            int caretY2 = r.y + r.h - 5;

            XSetForeground(app->disp, app->gc, app->colorBlack.pixel);
            XDrawLine(app->disp, app->win, app->gc, caretX, caretY1, caretX, caretY2);
            free(caretText);
        }
    }
}

// Draw button
void drawButton(AppState *app, Rect r, const char *text, bool hover)
{
    XColor bg = hover ? app->colorDarkBlue : app->colorBlue;
    drawRect(app, r, bg);
    drawRectBorder(app, r, app->colorBlack);

    int textWidth = drawText(app, text, 0, 0);
    int textX = r.x + (r.w - textWidth) / 2;
    int textY = r.y + (r.h - FONT_SIZE) / 2;

    XSetForeground(app->disp, app->gc, app->colorWhite.pixel);
    drawText(app, text, textX, textY);
}

// Redraw entire window
void redraw(AppState *app)
{
    drawRect(app, (Rect){0, 0, WIDTH, HEIGHT}, app->colorWhite);
    drawText(app, "Email Sender", WIDTH / 2 - 50, 30);

    drawInputField(app, "Recipient:", app->recipient, app->rectRecipient,
                   INPUT_RECIPIENT);
    drawInputField(app, "Subject:", app->subject, app->rectSubject,
                   INPUT_SUBJECT);
    drawInputField(app, "Message:", app->message, app->rectMessage,
                   INPUT_MESSAGE);
    drawInputField(app, "Attachment:", app->attachment, app->rectAttachment,
                   INPUT_ATTACHMENT);

    XEvent event;
    Window w;
    int x, y, rx, ry;
    unsigned int mask;
    XQueryPointer(app->disp, app->win, &w, &w, &x, &y, &rx, &ry, &mask);

    bool hoverAddRecip = (x >= app->btnAddRecipient.x &&
                          x < app->btnAddRecipient.x + app->btnAddRecipient.w &&
                          y >= app->btnAddRecipient.y &&
                          y < app->btnAddRecipient.y + app->btnAddRecipient.h);
    bool hoverAddAttach =
        (x >= app->btnAddAttachment.x &&
         x < app->btnAddAttachment.x + app->btnAddAttachment.w &&
         y >= app->btnAddAttachment.y &&
         y < app->btnAddAttachment.y + app->btnAddAttachment.h);
    bool hoverSend =
        (x >= app->btnSend.x && x < app->btnSend.x + app->btnSend.w &&
         y >= app->btnSend.y && y < app->btnSend.y + app->btnSend.h);
    bool hoverClear =
        (x >= app->btnClear.x && x < app->btnClear.x + app->btnClear.w &&
         y >= app->btnClear.y && y < app->btnClear.y + app->btnClear.h);

    drawButton(app, app->btnAddRecipient, "Add Recipient", hoverAddRecip);
    drawButton(app, app->btnAddAttachment, "Add Attachment", hoverAddAttach);
    drawButton(app, app->btnSend, "Send", hoverSend);
    drawButton(app, app->btnClear, "Clear", hoverClear);

    drawRect(app, app->rectStatus, app->colorLightGray);
    drawRectBorder(app, app->rectStatus, app->colorGray);
    drawText(app, "Status:", app->rectStatus.x, app->rectStatus.y - 20);
    drawText(app, app->status, app->rectStatus.x + TEXT_PADDING,
             app->rectStatus.y + TEXT_PADDING);

    drawText(app, "Tab: switch fields | I: toggle interrupt protection", MARGIN,
             HEIGHT - 30);
}

// Handle keyboard input
void handleKey(AppState *app, XKeyEvent *event)
{
    KeySym ks;
    char buf[16];
    XLookupString(event, buf, sizeof(buf), &ks, NULL);

    time_t now = time(NULL);
    if (now - app->caretTimer > 0.5)
    {
        app->showCaret = !app->showCaret;
        app->caretTimer = now;
    }

    if (app->activeField == INPUT_NONE)
        return;

    if (ks == XK_BackSpace)
    {
        char *text = NULL;
        int *pos = &app->caretPos[app->activeField];

        switch (app->activeField)
        {
        case INPUT_RECIPIENT:
            text = app->recipient;
            break;
        case INPUT_SUBJECT:
            text = app->subject;
            break;
        case INPUT_MESSAGE:
            text = app->message;
            break;
        case INPUT_ATTACHMENT:
            text = app->attachment;
            break;
        default:
            return;
        }

        if (*pos > 0)
        {
            memmove(&text[*pos - 1], &text[*pos], strlen(text) - *pos + 1);
            (*pos)--;
        }
        return;
    }

    if (ks == XK_Tab)
    {
        bool shift = (event->state & ShiftMask) != 0;
        app->activeField =
            shift ? (app->activeField - 1 + 4) % 4 : (app->activeField + 1) % 4;
        app->caretPos[app->activeField] =
            strlen(app->activeField == INPUT_RECIPIENT ? app->recipient
                   : app->activeField == INPUT_SUBJECT ? app->subject
                   : app->activeField == INPUT_MESSAGE ? app->message
                                                       : app->attachment);
        app->showCaret = true;
        app->caretTimer = now;
        return;
    }

    if (ks == XK_Left && app->caretPos[app->activeField] > 0)
    {
        app->caretPos[app->activeField]--;
        return;
    }
    if (ks == XK_Right)
    {
        int len = strlen(app->activeField == INPUT_RECIPIENT ? app->recipient
                         : app->activeField == INPUT_SUBJECT ? app->subject
                         : app->activeField == INPUT_MESSAGE ? app->message
                                                             : app->attachment);
        if (app->caretPos[app->activeField] < len)
            app->caretPos[app->activeField]++;
        return;
    }

    if (buf[0] >= 32 && buf[0] <= 126)
    {
        char *text = NULL;
        int maxLen = 0;
        int *pos = &app->caretPos[app->activeField];

        switch (app->activeField)
        {
        case INPUT_RECIPIENT:
            text = app->recipient;
            maxLen = 255;
            break;
        case INPUT_SUBJECT:
            text = app->subject;
            maxLen = 255;
            break;
        case INPUT_MESSAGE:
            text = app->message;
            maxLen = 2047;
            break;
        case INPUT_ATTACHMENT:
            text = app->attachment;
            maxLen = 255;
            break;
        default:
            return;
        }

        if (strlen(text) < maxLen)
        {
            memmove(&text[*pos + 1], &text[*pos], strlen(text) - *pos + 1);
            text[*pos] = buf[0];
            (*pos)++;
        }
    }
}

// Handle mouse click
void handleClick(AppState *app, XButtonEvent *event)
{
    InputField newField = INPUT_NONE;
    if (event->x >= app->rectRecipient.x &&
        event->x < app->rectRecipient.x + app->rectRecipient.w &&
        event->y >= app->rectRecipient.y &&
        event->y < app->rectRecipient.y + app->rectRecipient.h)
    {
        newField = INPUT_RECIPIENT;
    }
    else if (event->x >= app->rectSubject.x &&
             event->x < app->rectSubject.x + app->rectSubject.w &&
             event->y >= app->rectSubject.y &&
             event->y < app->rectSubject.y + app->rectSubject.h)
    {
        newField = INPUT_SUBJECT;
    }
    else if (event->x >= app->rectMessage.x &&
             event->x < app->rectMessage.x + app->rectMessage.w &&
             event->y >= app->rectMessage.y &&
             event->y < app->rectMessage.y + app->rectMessage.h)
    {
        newField = INPUT_MESSAGE;
    }
    else if (event->x >= app->rectAttachment.x &&
             event->x < app->rectAttachment.x + app->rectAttachment.w &&
             event->y >= app->rectAttachment.y &&
             event->y < app->rectAttachment.y + app->rectAttachment.h)
    {
        newField = INPUT_ATTACHMENT;
    }

    if (newField != INPUT_NONE)
    {
        app->activeField = newField;
        app->caretPos[newField] =
            strlen(newField == INPUT_RECIPIENT ? app->recipient
                   : newField == INPUT_SUBJECT ? app->subject
                   : newField == INPUT_MESSAGE ? app->message
                                               : app->attachment);
        app->showCaret = true;
        app->caretTimer = time(NULL);
        return;
    }

    app->activeField = INPUT_NONE;
    if (event->x >= app->btnAddRecipient.x &&
        event->x < app->btnAddRecipient.x + app->btnAddRecipient.w &&
        event->y >= app->btnAddRecipient.y &&
        event->y < app->btnAddRecipient.y + app->btnAddRecipient.h)
    {
        if (strlen(app->recipient) > 0)
        {
            SendEmailAddRecipient(app->emailState, app->recipient);
            sprintf(app->status, "Added recipient: %s", app->recipient);
            memset(app->recipient, 0, sizeof(app->recipient));
        }
        else
        {
            strcpy(app->status, "Recipient field empty!");
        }
    }
    else if (event->x >= app->btnAddAttachment.x &&
             event->x < app->btnAddAttachment.x + app->btnAddAttachment.w &&
             event->y >= app->btnAddAttachment.y &&
             event->y < app->btnAddAttachment.y + app->btnAddAttachment.h)
    {
        if (strlen(app->attachment) > 0)
        {
            char *err = SendEmailAddAttachment(app->emailState, app->attachment);
            if (err)
            {
                sprintf(app->status, "Attachment error: %s", err);
                free(err);
            }
            else
            {
                sprintf(app->status, "Added attachment: %s", app->attachment);
            }
            memset(app->attachment, 0, sizeof(app->attachment));
        }
        else
        {
            strcpy(app->status, "Attachment path empty!");
        }
    }
    else if (event->x >= app->btnSend.x &&
             event->x < app->btnSend.x + app->btnSend.w &&
             event->y >= app->btnSend.y &&
             event->y < app->btnSend.y + app->btnSend.h)
    {

        if (strlen(app->recipient) > 0)
        {
            SendEmailAddRecipient(app->emailState, app->recipient);
            memset(app->recipient, 0, sizeof(app->recipient));
        }

        SendEmailSetSubject(app->emailState, app->subject);
        SendEmailSetMessageText(app->emailState, app->message);

        char *err = SendEmailSend(app->emailState);
        if (err)
        {
            if (strstr(err, "no recipients specified"))
            {
                sprintf(app->status,
                        "Send failed: No recipients added! Enter a recipient first.");
            }
            else
            {
                sprintf(app->status, "Send failed: %s", err);
            }
            free(err);
        }
        else
        {
            strcpy(app->status, "Email sent successfully!");
            memset(app->recipient, 0, sizeof(app->recipient));
            memset(app->subject, 0, sizeof(app->subject));
            memset(app->message, 0, sizeof(app->message));
            memset(app->attachment, 0, sizeof(app->attachment));
        }
    }
    else if (event->x >= app->btnClear.x &&
             event->x < app->btnClear.x + app->btnClear.w &&
             event->y >= app->btnClear.y &&
             event->y < app->btnClear.y + app->btnClear.h)
    {
        memset(app->recipient, 0, sizeof(app->recipient));
        memset(app->subject, 0, sizeof(app->subject));
        memset(app->message, 0, sizeof(app->message));
        memset(app->attachment, 0, sizeof(app->attachment));
        strcpy(app->status, "Inputs cleared");
    }
}

// Main event loop
void run(AppState *app)
{
    XEvent event;
    while (1)
    {
        XNextEvent(app->disp, &event);
        switch (event.type)
        {
        case Expose:
            redraw(app);
            break;
        case KeyPress:
            if (event.xkey.keycode == XKeysymToKeycode(app->disp, XK_i))
            {
                app->ignoreInterrupts = !app->ignoreInterrupts;
                if (app->ignoreInterrupts)
                {
                    SendEmailIgnoreInterrupts();
                    strcpy(app->status, "Interrupts ignored");
                }
                else
                {
                    strcpy(app->status, "Interrupts enabled");
                }
            }
            else
            {
                handleKey(app, &event.xkey);
            }
            redraw(app);
            break;
        case ButtonPress:
            handleClick(app, &event.xbutton);
            redraw(app);
            break;
        case ConfigureNotify:
            redraw(app);
            break;
        case ClientMessage:
            if ((Atom)event.xclient.data.l[0] ==
                XInternAtom(app->disp, "WM_DELETE_WINDOW", False))
            {
                return;
            }
            break;
        }
    }
}

// Cleanup resources (fixed: use X11's None instead of NULL for Window)
void cleanup(AppState *app)
{
    if (app->emailState != NULL)
    {
        if (app->emailState->sender_email != NULL)
        {
            free(app->emailState->sender_email);
            app->emailState->sender_email = NULL;
        }
        if (app->emailState->sender_key != NULL)
        {
            free(app->emailState->sender_key);
            app->emailState->sender_key = NULL;
        }
        if (app->emailState->smtp_server != NULL)
        {
            free(app->emailState->smtp_server);
            app->emailState->smtp_server = NULL;
        }
        SendEmailCleanup(app->emailState);
    }

    if (app->face != NULL)
        FT_Done_Face(app->face);
    if (app->ftLib != NULL)
        FT_Done_FreeType(app->ftLib);
    if (app->gc != NULL)
        XFreeGC(app->disp, app->gc);
    if (app->win != None)
        XDestroyWindow(app->disp, app->win); // Fixed: Use None instead of NULL
    if (app->disp != NULL)
        XCloseDisplay(app->disp);
}

int main()
{
    AppState app = {0};
    app.win = None; // Initialize window to X11's invalid value
    if (!initApp(&app))
    {
        cleanup(&app);
        return 1;
    }

    Atom wmDelete = XInternAtom(app.disp, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(app.disp, app.win, &wmDelete, 1);

    run(&app);
    cleanup(&app);
    return 0;
}
